
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include "ntp.h"

struct tm * ntp_get_time (const char * server) {
    printf("ntp: try to get time from %s\n", server);

    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockfd < 0) {
        printf("ntp: create socket error\n");
        return NULL;
    }

    struct hostent * ent = gethostbyname(server);
    if (!ent) {
        printf("resolve host name failed.");
        goto failed;
    }

    char str[INET_ADDRSTRLEN];
    printf("server ip is: %s\n", inet_ntop(ent->h_addrtype, ent->h_addr, str, sizeof(str)));

    struct timeval tmo;
    tmo.tv_sec = NTP_REQ_TMO;
    tmo.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmo, sizeof(tmo));

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(NTP_SERVER_PORT);
    addr.sin_addr = *(struct in_addr *)ent->h_addr;
    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        printf("ntp: connect to ntp server failed.");
        goto failed;
    }

    for (int i = 0; i < NTP_REQ_RETRY; i++) {
        ntp_pkt_t pkt;
        memset(&pkt, 0, sizeof(pkt));
        pkt.LI_VN_Mode = (NTP_MODE << 0) | (NTP_VERSION << 3);

        if (send(sockfd, (const void *)&pkt, sizeof(ntp_pkt_t), 0) < 0) {
            printf("ntp: send to ntp failed.\n");
            break;
        }

        memset(&pkt, 0, sizeof(pkt));
        if (recv(sockfd, (void *)&pkt, sizeof(ntp_pkt_t), 0) < 0) {
            printf("ntp: recv failed.\n");
            continue;
        }

        pkt.trans_ts.seconds = ntohl(pkt.trans_ts.seconds);

        // 1970.1.1. - 1900.1.1
        time_t t = pkt.trans_ts.seconds - NTP_TIME_1900_1970;
        close(sockfd);
        return localtime(&t);
    }
failed:
    close(sockfd);
    return NULL;
}
